package org.neptune.service.config;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import com.alibaba.druid.pool.DruidDataSource;

@ConfigurationProperties(prefix = "primary.datasource.druid")
@Configuration
public class PrimaryDatasourceConfig {

	/** mapper文件的相对路径 */
	private static final String MAPPER_LOCATION = "classpath:mapper/*.xml";

	private String filters;

	private String url;

	private String username;

	private String password;

	private String driverClassName;

	private int initialSize;

	private int minIdle;

	private int maxActive;

	private long maxWait;

	private long timeBetweenEvictionRunsMillis;

	private long minEvictableIdleTimeMillis;

	private String validationQuery;

	private boolean testWhileIdle;

	private boolean testOnBorrow;

	private boolean testOnReturn;

	private boolean poolPreparedStatements;

	private int maxPoolPreparedStatementPerConnectionSize;

	public String getFilters() {
		return filters;
	}

	public void setFilters(String filters) {
		this.filters = filters;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getDriverClassName() {
		return driverClassName;
	}

	public void setDriverClassName(String driverClassName) {
		this.driverClassName = driverClassName;
	}

	public int getInitialSize() {
		return initialSize;
	}

	public void setInitialSize(int initialSize) {
		this.initialSize = initialSize;
	}

	public int getMinIdle() {
		return minIdle;
	}

	public void setMinIdle(int minIdle) {
		this.minIdle = minIdle;
	}

	public int getMaxActive() {
		return maxActive;
	}

	public void setMaxActive(int maxActive) {
		this.maxActive = maxActive;
	}

	public long getMaxWait() {
		return maxWait;
	}

	public void setMaxWait(long maxWait) {
		this.maxWait = maxWait;
	}

	public long getTimeBetweenEvictionRunsMillis() {
		return timeBetweenEvictionRunsMillis;
	}

	public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
		this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
	}

	public long getMinEvictableIdleTimeMillis() {
		return minEvictableIdleTimeMillis;
	}

	public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
		this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
	}

	public String getValidationQuery() {
		return validationQuery;
	}

	public void setValidationQuery(String validationQuery) {
		this.validationQuery = validationQuery;
	}

	public boolean isTestWhileIdle() {
		return testWhileIdle;
	}

	public void setTestWhileIdle(boolean testWhileIdle) {
		this.testWhileIdle = testWhileIdle;
	}

	public boolean isTestOnBorrow() {
		return testOnBorrow;
	}

	public void setTestOnBorrow(boolean testOnBorrow) {
		this.testOnBorrow = testOnBorrow;
	}

	public boolean isTestOnReturn() {
		return testOnReturn;
	}

	public void setTestOnReturn(boolean testOnReturn) {
		this.testOnReturn = testOnReturn;
	}

	public boolean isPoolPreparedStatements() {
		return poolPreparedStatements;
	}

	public void setPoolPreparedStatements(boolean poolPreparedStatements) {
		this.poolPreparedStatements = poolPreparedStatements;
	}

	public int getMaxPoolPreparedStatementPerConnectionSize() {
		return maxPoolPreparedStatementPerConnectionSize;
	}

	public void setMaxPoolPreparedStatementPerConnectionSize(
			int maxPoolPreparedStatementPerConnectionSize) {
		this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
	}

	// 主数据源使用@Primary注解进行标识
	@Primary
	@Bean(name = "primaryDataSource")
	public DataSource primaryDataSource() throws SQLException {
		DruidDataSource druid = new DruidDataSource();
		// 监控统计拦截的filters
		druid.setFilters(filters);

		// 配置基本属性
		druid.setDriverClassName(driverClassName);
		druid.setUsername(username);
		druid.setPassword(password);
		druid.setUrl(url);

		// 初始化时建立物理连接的个数
		druid.setInitialSize(initialSize);
		// 最大连接池数量
		druid.setMaxActive(maxActive);
		// 最小连接池数量
		druid.setMinIdle(minIdle);
		// 获取连接时最大等待时间，单位毫秒。
		druid.setMaxWait(maxWait);
		// 间隔多久进行一次检测，检测需要关闭的空闲连接
		druid.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
		// 一个连接在池中最小生存的时间
		druid.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
		// 用来检测连接是否有效的sql
		druid.setValidationQuery(validationQuery);
		// 建议配置为true，不影响性能，并且保证安全性。
		druid.setTestWhileIdle(testWhileIdle);
		// 申请连接时执行validationQuery检测连接是否有效
		druid.setTestOnBorrow(testOnBorrow);
		druid.setTestOnReturn(testOnReturn);
		// 是否缓存preparedStatement，也就是PSCache，oracle设为true，mysql设为false。分库分表较多推荐设置为false
		druid.setPoolPreparedStatements(poolPreparedStatements);
		// 打开PSCache时，指定每个连接上PSCache的大小
		druid.setMaxPoolPreparedStatementPerConnectionSize(
				maxPoolPreparedStatementPerConnectionSize);

		return druid;
	}

	// 创建该数据源的事务管理
	@Primary
	@Bean(name = "primaryTransactionManager")
	public DataSourceTransactionManager primaryTransactionManager() throws SQLException {
		return new DataSourceTransactionManager(primaryDataSource());
	}

	// 创建Mybatis的连接会话工厂实例
	@Primary
	@Bean(name = "primarySqlSessionFactory")
	public SqlSessionFactory primarySqlSessionFactory(
			@Qualifier("primaryDataSource") DataSource primaryDataSource) throws Exception {
		final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
		sessionFactory.setDataSource(primaryDataSource); // 设置数据源bean
		sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
				.getResources(PrimaryDatasourceConfig.MAPPER_LOCATION)); // 设置mapper文件路径

		return sessionFactory.getObject();
	}
}
